///////////////////////////////////////////////////////////////////////////////
//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
//
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//  License for the specific language governing rights and limitations
//  under the License.
//
//  The Original Code is MP4v2.
//
//  The Initial Developer of the Original Code is Kona Blend.
//  Portions created by Kona Blend are Copyright (C) 2008.
//  All Rights Reserved.
//
//  Contributors:
//      Kona Blend, kona8lend@@gmail.com
//
///////////////////////////////////////////////////////////////////////////////

#include "impl.h"

namespace mp4v2
{
    namespace impl
    {
        namespace qtff
        {

            ///////////////////////////////////////////////////////////////////////////////

            namespace
            {
                const string BOX_CODE = "colr";

                bool findColorParameterBox( MP4FileHandle file, MP4Atom &coding, MP4Atom *&colr );
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::add( MP4FileHandle file, uint16_t trackIndex, const Item &item )
            {
                MP4Atom *coding;

                if( !MP4_IS_VALID_FILE_HANDLE( file ))
                    throw new Exception( "invalid file handle", __FILE__, __LINE__, __FUNCTION__ );

                if( findCoding( file, trackIndex, coding ))
                    throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Atom *colr;
                if( !findColorParameterBox( file, *coding, colr ))
                    throw new Exception( "colr-box already exists", __FILE__, __LINE__, __FUNCTION__ );

                colr = MP4Atom::CreateAtom( *((MP4File *)file), coding, BOX_CODE.c_str() );
                coding->AddChildAtom( colr );
                colr->Generate();

                MP4StringProperty    *type;
                MP4Integer16Property *primariesIndex;
                MP4Integer16Property *transferFunctionIndex;
                MP4Integer16Property *matrixIndex;

                if( colr->FindProperty( "colr.colorParameterType", (MP4Property **)&type ))
                    type->SetValue( "nclc" );

                if( colr->FindProperty( "colr.primariesIndex", (MP4Property **)&primariesIndex ))
                    primariesIndex->SetValue( item.primariesIndex );

                if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property **)&transferFunctionIndex ))
                    transferFunctionIndex->SetValue( item.transferFunctionIndex );

                if( colr->FindProperty( "colr.matrixIndex", (MP4Property **)&matrixIndex ))
                    matrixIndex->SetValue( item.matrixIndex );

                return false;
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::add( MP4FileHandle file, MP4TrackId trackId, const Item &item )
            {
                MP4File &mp4 = *((MP4File *)file);
                return add( file, mp4.FindTrackIndex( trackId ), item );
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::get( MP4FileHandle file, uint16_t trackIndex, Item &item )
            {
                item.reset();

                MP4Atom *coding;
                if( findCoding( file, trackIndex, coding ))
                    throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Atom *colr;
                if( findColorParameterBox( file, *coding, colr ))
                    throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Integer16Property *primariesIndex;
                MP4Integer16Property *transferFunctionIndex;
                MP4Integer16Property *matrixIndex;

                if( colr->FindProperty( "colr.primariesIndex", (MP4Property **)&primariesIndex ))
                    item.primariesIndex = primariesIndex->GetValue();

                if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property **)&transferFunctionIndex ))
                    item.transferFunctionIndex = transferFunctionIndex->GetValue();

                if( colr->FindProperty( "colr.matrixIndex", (MP4Property **)&matrixIndex ))
                    item.matrixIndex = matrixIndex->GetValue();

                return false;
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::get( MP4FileHandle file, MP4TrackId trackId, Item &item )
            {
                MP4File &mp4 = *((MP4File *)file);
                return get( file, mp4.FindTrackIndex( trackId ), item );
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::list( MP4FileHandle file, ItemList &itemList )
            {
                itemList.clear();
                MP4File &mp4 = *((MP4File *)file);

                const uint16_t trackc = mp4.GetNumberOfTracks();
                for( uint16_t i = 0; i < trackc; i++)
                {
                    MP4TrackId id = mp4.FindTrackId( i );
                    if( id == MP4_INVALID_TRACK_ID )
                        continue;

                    const char *type = mp4.GetTrackType( id );
                    if( !type )
                        continue;

                    itemList.resize( itemList.size() + 1 );
                    IndexedItem &xitem = itemList[itemList.size() - 1];

                    xitem.trackIndex = i;
                    xitem.trackId    = id;

                    bool success = false;
                    try
                    {
                        success = !get( file, i, xitem.item );
                    }
                    catch( Exception *x )
                    {
                        delete x;
                    }

                    if( !success )
                    {
                        itemList.resize( itemList.size() - 1 );
                        continue;
                    }
                }

                return false;
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::remove( MP4FileHandle file, uint16_t trackIndex )
            {
                MP4Atom *coding;
                if( findCoding( file, trackIndex, coding ))
                    throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Atom *colr;
                if( findColorParameterBox( file, *coding, colr ))
                    throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );

                coding->DeleteChildAtom( colr );
                delete colr;

                return false;
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::remove( MP4FileHandle file, MP4TrackId trackId )
            {
                MP4File &mp4 = *((MP4File *)file);
                return remove( file, mp4.FindTrackIndex( trackId ));
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::set( MP4FileHandle file, uint16_t trackIndex, const Item &item )
            {
                MP4Atom *coding;
                if( findCoding( file, trackIndex, coding ))
                    throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Atom *colr;
                if( findColorParameterBox( file, *coding, colr ))
                    throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );

                MP4Integer16Property *primariesIndex;
                MP4Integer16Property *transferFunctionIndex;
                MP4Integer16Property *matrixIndex;

                if( colr->FindProperty( "colr.primariesIndex", (MP4Property **)&primariesIndex ))
                    primariesIndex->SetValue( item.primariesIndex );

                if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property **)&transferFunctionIndex ))
                    transferFunctionIndex->SetValue( item.transferFunctionIndex );

                if( colr->FindProperty( "colr.matrixIndex", (MP4Property **)&matrixIndex ))
                    matrixIndex->SetValue( item.matrixIndex );

                return false;
            }

            ///////////////////////////////////////////////////////////////////////////////

            bool
            ColorParameterBox::set( MP4FileHandle file, MP4TrackId trackId, const Item &item )
            {
                MP4File &mp4 = *((MP4File *)file);
                return set( file, mp4.FindTrackIndex( trackId ), item );
            }

            ///////////////////////////////////////////////////////////////////////////////

            ColorParameterBox::IndexedItem::IndexedItem()
                : trackIndex ( numeric_limits<uint16_t>::max() )
                , trackId    ( MP4_INVALID_TRACK_ID )
            {
            }

            ///////////////////////////////////////////////////////////////////////////////

            ColorParameterBox::Item::Item()
                : primariesIndex        ( 6 )
                , transferFunctionIndex ( 1 )
                , matrixIndex           ( 6 )
            {
            }

            ///////////////////////////////////////////////////////////////////////////////

            void
            ColorParameterBox::Item::convertFromCSV( const string &text )
            {
                istringstream iss( text );
                char delim;

                iss >> primariesIndex;
                iss >> delim;
                iss >> transferFunctionIndex;
                iss >> delim;
                iss >> matrixIndex;

                // input was good if we end up with only eofbit set
                if( iss.rdstate() != ios::eofbit )
                {
                    reset();
                    ostringstream xss;
                    xss << "invalid ColorParameterBox format"
                        << " (expecting: INDEX1,INDEX2,INDEX3)"
                        << " got: " << text;
                    throw new Exception( xss.str(), __FILE__, __LINE__, __FUNCTION__ );
                }
            }

            ///////////////////////////////////////////////////////////////////////////////

            string
            ColorParameterBox::Item::convertToCSV() const
            {
                string buffer;
                return convertToCSV( buffer );
            }

            ///////////////////////////////////////////////////////////////////////////////

            string &
            ColorParameterBox::Item::convertToCSV( string &buffer ) const
            {
                ostringstream oss;
                oss << primariesIndex << ',' << transferFunctionIndex << ',' << matrixIndex;
                buffer = oss.str();
                return buffer;
            }

            ///////////////////////////////////////////////////////////////////////////////

            void
            ColorParameterBox::Item::reset()
            {
                primariesIndex        = 6;
                transferFunctionIndex = 1;
                matrixIndex           = 6;
            }

            ///////////////////////////////////////////////////////////////////////////////

            namespace
            {

                ///////////////////////////////////////////////////////////////////////////////

                bool
                findColorParameterBox( MP4FileHandle file, MP4Atom &coding, MP4Atom *&colr )
                {
                    colr = NULL;

                    MP4Atom *found = NULL;
                    const uint32_t atomc = coding.GetNumberOfChildAtoms();
                    for( uint32_t i = 0; i < atomc; i++ )
                    {
                        MP4Atom *atom = coding.GetChildAtom( i );
                        if( BOX_CODE != atom->GetType() )
                            continue;
                        found = atom;
                    }
                    if( !found )
                        return true;

                    MP4StringProperty *type;
                    if( !found->FindProperty( "colr.colorParameterType", (MP4Property **)&type ))
                        return true;

                    const string type_nclc = "nclc";
                    if( type_nclc != type->GetValue() )
                        return true;

                    colr = found;
                    return false;
                }

                ///////////////////////////////////////////////////////////////////////////////

            } // anonymous

            ///////////////////////////////////////////////////////////////////////////////

        }
    }
} // namespace mp4v2::impl::qtff
